package base;

import static base.Edge.NOT_TURN_OFF;
import static base.Edge.NOT_TURN_ON;
import static base.Edge.TURN_OFF;
import static base.Edge.TURN_ON;
import static base.Edge.fixed;
import static base.Edge.turnOffOrNot;
import static base.Edge.turnOnOrNot;
import static base.InverterUtils.finalizeOffPrecursors;
import static base.InverterUtils.finalizeOnPrecursors;
import static base.InverterUtils.processOffPrecursors;
import static base.InverterUtils.processOnPrecursors;
import static base.InverterUtils.termPredicate;
import static base.VertexState.OFF;
import static base.VertexState.ON;
import static com.google.common.base.Predicates.in;
import static com.google.common.collect.Iterables.concat;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
import static java.util.Collections.disjoint;

import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;

public class InvertVertexBuilder implements Callable<InvertVertexBuilder.InvertVertex> {

	final ImmutableDynamic dynamic;
	final Integer vertex;

	final SetMultimap<Edge, Integer> knownEdges;
	
	InvertVertexBuilder(ImmutableDynamic forDynamic, Integer forVertex) {
		this(forDynamic,forVertex,null);
	}
	
	InvertVertexBuilder(ImmutableDynamic forDynamic, Integer forVertex, Support known) {
		dynamic = forDynamic;
		vertex = forVertex;
		knownEdges = known==null ? ImmutableSetMultimap.<Edge,Integer>of() : known.fixedInto(forVertex).fixedComponent;
	}
	
	InvertVertex result;
	@Override public InvertVertex call() { 
		return result == null ? (result = InvertVertex.make(dynamic,vertex,knownEdges)) : result;
	}
	
	
	/**
	 * 
	 * @author cap10
	 *
	 */
	public static class InvertVertex implements Function<Entry<SupportState,SupportState>,InvertVertex>, Callable<Long> {
						
		static InvertVertex make(
			final ImmutableDynamic dynamic,
			final Integer forVertex,
			final SetMultimap<Edge, Integer> knownEdges)
		{

			final SortedSet<SortedSet<Integer>> gTerms = Sets.newTreeSet(SmallestFirst.INSTANCE);
			final TreeMultimap<SortedSet<Integer>,Integer> rTerms = TreeMultimap.create(SmallestFirst.INSTANCE, Ordering.natural());
			final SortedSet<SortedSet<Integer>> justR = Sets.newTreeSet(SmallestFirst.INSTANCE);
			
			final Set<Integer> knownR = Sets.newHashSet(knownEdges.get(TURN_OFF));
			final Set<Integer> knownG = Sets.newHashSet(knownEdges.get(TURN_ON) );
			final Set<Integer> knownGBar = Sets.newHashSet(knownEdges.get(NOT_TURN_ON));
			final Set<Integer> knownRBar = Sets.newHashSet(knownEdges.get(NOT_TURN_OFF));
			
			final Predicate<Set<Integer>> gFalse = termPredicate(knownG,gTerms);
			
			boolean nogo =
				!processOnPrecursors(  knownR, knownGBar, knownRBar, knownG, gTerms, dynamic.precursors(forVertex).get(ON) ).isEmpty();
						
			nogo |=	!processOffPrecursors( knownR, knownGBar, knownRBar, knownG, rTerms, dynamic.precursors(forVertex).get(OFF)).isEmpty();

			nogo |= !finalizeOnPrecursors( gTerms, knownGBar, knownG).isEmpty();
			
			nogo |= !finalizeOffPrecursors(rTerms, knownR,    justR, gFalse).isEmpty();
			
			if ( nogo ) return new Unsatisfiable();		
			
			final ImmutableSetMultimap<Edge, Integer> fixedComponent = ImmutableSetMultimap.<Edge, Integer>builder()
				.putAll(TURN_ON , knownG) // a subset of knownRBar
				.putAll(TURN_OFF, knownR) // a subset of knownGBar
				.putAll(NOT_TURN_ON ,  knownGBar) // contains knownR, does not contain any do_nothing
				.putAll(NOT_TURN_OFF,  knownRBar) // contains knownG, does not contain any do_nothing
				.build();
			
			return new InvertVertex(true,fixedComponent,gTerms,justR,rTerms,dynamic,forVertex);
	
		}

		final ImmutableSortedSet<SortedSet<Integer>> gTerms;
		final ImmutableSortedSet<SortedSet<Integer>> justR;
		final TreeMultimap<SortedSet<Integer>, Integer> rTerms;
		final ImmutableDynamic dynamic;
		final Integer vertex;

		InvertVertex(
			boolean isFeasible,
			ImmutableSetMultimap<Edge,Integer> fixedComponent,
			SortedSet<SortedSet<Integer>> gTerms,
			SortedSet<SortedSet<Integer>> justR,
			TreeMultimap<SortedSet<Integer>, Integer> rTerms,
			ImmutableDynamic dynamic,
			Integer vertex)
		{
			
			this.isFeasible = isFeasible;
			this.fixedComponent = fixedComponent;
			this.gTerms = fixTerms(gTerms);
			this.justR  = fixTerms(justR);
			this.rTerms = rTerms;
			this.dynamic = dynamic;
			this.vertex = vertex;
		}
		
		private static ImmutableSortedSet<SortedSet<Integer>> fixTerms(SortedSet<SortedSet<Integer>> toFix) {
			ImmutableSortedSet.Builder<SortedSet<Integer>> builder = 
				ImmutableSortedSet.orderedBy(SmallestFirst.INSTANCE);
			for (SortedSet<Integer> term : toFix) builder.add(ImmutableSortedSet.copyOf(term));
			return builder.build();
		}
		
		private final boolean isFeasible;
		public boolean isFeasible() { return isFeasible; };
		
		final ImmutableSetMultimap<Edge,Integer> fixedComponent;
		public ImmutableSetMultimap<Edge,Integer> fixedComponent() { return fixedComponent; }
		
		Set<Integer> g() { return fixedComponent.get(TURN_ON); }
		Set<Integer> r() { return fixedComponent.get(TURN_OFF); }
		private Set<Integer> rBar() { return fixedComponent.get(NOT_TURN_OFF); }
		Set<Integer> gBar() { return fixedComponent.get(NOT_TURN_ON); }
		
//		final Predicate<SetMultimap<Edge,Integer>> satisfying = new Predicate<SetMultimap<Edge,Integer>>() {
//			@Override public boolean apply(SetMultimap<Edge,Integer> input) {
//					final Set<Integer> on = input.get(TURN_ON);
//					for (final Set<Integer> gTerm : gTerms) if (Collections.disjoint(on, gTerm)) return false;
//					
//					final Set<Integer> off = input.get(TURN_OFF);
//					for (final Set<Integer> rs : justR)
//						if (Collections.disjoint(rs, off)) return false;
//					
//					for (final SortedSet<Integer> rs : rTerms.keySet())
//						if (Collections.disjoint(rs, off) && !Collections.disjoint(rTerms.get(rs), on)) return false;
//					
//					return true;
//				}
//			};
			
		final Predicate<Set<Integer>> gSatFilter = new Predicate<Set<Integer>>() {
				@Override public boolean apply(Set<Integer> input) {
					for (final Set<Integer> gTerm : gTerms) if (Collections.disjoint(input, gTerm)) return false;
					return true;
				}
			};
			
		final Predicate<FlexibleComponent> rSatFilter = new Predicate<FlexibleComponent>() {
			@Override public boolean apply(FlexibleComponent input) {
					Set<Integer> r = input.get(TURN_OFF);
					for (final Set<Integer> rs : justR) if (Collections.disjoint(rs, r)) return false;
					
					Set<Integer> g = input.get(TURN_ON);
					for (final SortedSet<Integer> rs : rTerms.keySet())
						if (Collections.disjoint(rs, r) && !Collections.disjoint(rTerms.get(rs), g)) return false;

					return true;
				}
			};
		
		private ImmutableSortedSet<FlexibleComponent> minimalEnumeratedComponents;
		
		public ImmutableSortedSet<FlexibleComponent> minimalEnumeratedComponents() {
			//TODO eliminate self-edge checking for case where self-edge is already fixed or forbidden
			if (minimalEnumeratedComponents==null) {
				enumCount=0l;
				Iterator<FlexibleComponent> iterator = enumerated().iterator();
				if (!iterator.hasNext()) {
					minimalEnumeratedComponents = ImmutableSortedSet.<FlexibleComponent>of(FlexibleComponent.Empty.INSTANCE);
					enumCount++;
				} else {
					SortedSet<FlexibleComponent> smallest = Sets.newTreeSet();
					FlexibleComponent hold = iterator.next();
					smallest.add(hold);
					boolean smallestHasSelfEdge = hold.get(TURN_ON).contains(vertex);
					enumCount++;
					int delta;
					while (iterator.hasNext()) {
						hold = iterator.next();
						delta = smallest.first().size() - hold.size();
						if ( delta > 1) {
							smallest.clear();
							smallest.add(hold);
							smallestHasSelfEdge = hold.get(TURN_ON).contains(vertex);
						} else if (delta >= 0) {
							if (delta == 0) {
								smallest.add(hold);
								smallestHasSelfEdge |= hold.get(TURN_ON).contains(vertex);
							} else {
								(!smallestHasSelfEdge ? smallest : smallest.tailSet(FlexibleComponents.marker(smallest.first())))
									.clear();
								smallest.add(hold);
								smallestHasSelfEdge = hold.get(TURN_ON).contains(vertex);
							}
						} else if (!smallestHasSelfEdge && delta==-1 && hold.get(TURN_ON).contains(vertex)) {
							smallest.add(hold);
						}
						enumCount++;
					}
					if (smallestHasSelfEdge) {
						smallest.tailSet(FlexibleComponents.marker(smallest.first())).clear();
					} else {
						Iterator<FlexibleComponent> iter = smallest.tailSet(FlexibleComponents.marker(smallest.first())).iterator();
						while (iter.hasNext()) if (!iter.next().get(TURN_ON).contains(vertex)) iter.remove();
					}
					minimalEnumeratedComponents = ImmutableSortedSet.copyOfSorted(smallest);
				}
			}
			
			return minimalEnumeratedComponents;
		}
		
		private long enumCount = -1l;
		public long enumCount() {
			if (enumCount < 0l) minimalEnumeratedComponents();
			return enumCount;
		}
		
		private long designability = -1;
		public long designability() {
			if (designability < 0) {
				if (!isFeasible) return (designability = 0); else designability = 1l;
				if (enumCount()!=0) designability *= enumCount;
				designability = designability << pow2();
				for (int i=0; i<pow3(); i++) designability *= 3;
			}
			return designability;
		}
		
		private static final double log10_2 = Math.log10(2d);
		private static final double log10_3 = Math.log10(3d);
		private double logD = -1;
		public double logD() {
			if (!isFeasible) return logD;
			
			if (logD<0d) {
				logD = 0;
				if (enumCount()!=0) logD+= Math.log10(enumCount);
				logD += pow2()*log10_2 + pow3()*log10_3;
			}
			
			return logD;
		}
		
		public Iterable<FlexibleComponent> enumerated() {
			Set<Integer> gMerge = gMerge();		
			Set<Integer> rMerge = rMerge();
			
			Set<Integer> onlyG = difference(gMerge,rMerge);
			Set<Integer> onlyR = difference(rMerge,gMerge);
			Set<Integer> intersect = intersection(rMerge,gMerge);
			
			return FlexibleComponents.combine(onlyR, onlyG, intersect, gSatFilter, FlexibleComponents.mixedPredicate(rTerms, justR) );
			
		}
				
//		@SuppressWarnings("unchecked")
//		public Iterable<SetMultimap<Edge,Integer>> enumerated() {
//			if (gTerms.isEmpty() && justR.isEmpty() && rTerms.isEmpty()) { 
//				return ImmutableSortedSet.of();
//			} else {
//				List<Integer> enumVertices	= Lists.newArrayList();
//				List<Set<Edge>> options		= Lists.newArrayList();
//
//				Set<Integer> gMerge = gMerge();
//				
//				Set<Integer> rMerge = rMerge();
//								
//				for (Integer vertex : dynamic.vertices())
//					if (gMerge.contains(vertex) || rMerge.contains(vertex)) {
//						enumVertices.add(vertex);
//						options.add(gMerge.contains(vertex) ? (rMerge.contains(vertex) ? Edge.fixed : Edge.turnOnOrNot) 
//											: Edge.turnOffOrNot);
//				}
//				
//				return filter(transform(cartesianProduct(options),enumTransformer(enumVertices)), satisfying);
//				
//			}
//		}
		
//		private static Function<List<Edge>,SetMultimap<Edge,Integer>> enumTransformer(final List<Integer> enumVertices) {
//			return new Function<List<Edge>,SetMultimap<Edge,Integer>>() {
//				@Override public SetMultimap<Edge, Integer> apply(List<Edge> s) {
//					SetMultimap<Edge,Integer> possibleSolution = HashMultimap.create();
//					int i=0;
//					for (Edge e : s)
//						if (e!=DO_NOTHING) { possibleSolution.put(e, enumVertices.get(i++)); }
//						else i++;
//					
//					return possibleSolution;
//				}
//			};
//		}
		
		private int pow2=-1;
		private int pow3=-1;
		public int pow2() { 
			if (pow2 < 0) decorativeComponent();
			return pow2;
		}
		
		public int pow3() { 
			if (pow3 < 0) decorativeComponent();
			return pow3;
		}
		
		private ImmutableSetMultimap<Integer,Edge> decorativeComponent;
		public ImmutableSetMultimap<Integer,Edge> decorativeComponent() { 
			if (decorativeComponent==null) {
				pow2=0;
				pow3=0;
				if (!isFeasible) {
					decorativeComponent = ImmutableSetMultimap.of();
					return decorativeComponent;
				}
				
				ImmutableSetMultimap.Builder<Integer,Edge> builder = ImmutableSetMultimap.builder();
				if (gTerms.isEmpty() && justR.isEmpty() && rTerms.isEmpty()) {

					for (Integer vertex : filter(dynamic.vertices(), Predicates.not(in(fixedComponent.values())))) {
						pow3++;
						builder.putAll(vertex, fixed);
					}
					for (Integer vertex : difference(rBar(), union(gBar(),g()) ) ) {
						pow2++;
						builder.putAll(vertex, turnOnOrNot);
					}
					for (Integer vertex : difference(gBar(), union(rBar(),r()) ) ) {
						pow2++;
						builder.putAll(vertex, turnOffOrNot);
					}
				} else {
					
					Set<Integer> gMerge = gMerge();
					Set<Integer> rMerge = rMerge();

					for (Integer vertex : Iterables.filter(dynamic.vertices(), Predicates.not(in(fixedComponent.values())))) {
						if (gMerge.contains(vertex) && rMerge.contains(vertex)) {
							//do nothing
						} else if (rMerge.contains(vertex)) { //TODO: implies gMerge+gBar must contain?
							builder.putAll(vertex, turnOnOrNot);
							pow2++;
						} else if (gMerge.contains(vertex)) {
							builder.putAll(vertex, turnOffOrNot);
							pow2++;
						} else {
							builder.putAll(vertex, fixed);
							pow3++;
						}
					}
					for (Integer vertex : difference(rBar(),gBar())) {
						if (!union(gMerge,g()).contains(vertex)) {
							builder.putAll(vertex, turnOnOrNot);
							pow2++;
						}
					}
					for (Integer vertex : difference(gBar(),rBar())) {
						if (!union(rMerge,r()).contains(vertex)) {
							builder.putAll(vertex, turnOffOrNot);
							pow2++;
						}
					}
				}
				decorativeComponent = builder.build();
			}
			return decorativeComponent;
		}
		
		private Set<Integer> gMerge;
		public Set<Integer> gMerge() {
			if (gMerge == null) {
				this.gMerge = ImmutableSet.copyOf(filter(concat(rTerms.values(),concat(gTerms),concat(justR)),Predicates.not(in(gBar()))));
			}
			return gMerge;
		}
		
		private Set<Integer> rMerge;
		public Set<Integer> rMerge() {
			if (rMerge == null) {
				this.rMerge = ImmutableSet.copyOf(filter(concat(concat(justR),concat(rTerms.keySet()), rTerms.values()), Predicates.not(in(rBar()))));
			}
			return rMerge;
		}
		
		public Callable<InvertVertex> apply(final SupportState from, final VertexState to) {
			return new Callable<InvertVertex>() {
				@Override public InvertVertex call() {
					return to == ON ? reprocessForON(from.get(ON)) : reprocessForOFF(from.get(ON));
				}
			};
		}
		
		public Long call() { return enumCount(); }
		
		public InvertVertex apply(SupportState from, SupportState to) {
			
			if (from==null) throw new NullPointerException();
			if (!dynamic.vertices().equals(from.get())) throw new IllegalArgumentException();
			if (dynamic.get(from)!=null) throw new IllegalArgumentException();
			
			if (to==null) throw new NullPointerException();
			if (!dynamic.vertices().equals(to.get())) throw new IllegalArgumentException();
			
			return to.get(ON).contains(vertex) ? reprocessForON(from.get(ON)) : reprocessForOFF(from.get(ON));
		}
				
		public InvertVertex apply(Entry<SupportState,SupportState> newTransition) {
			return apply(newTransition.getKey(),newTransition.getValue());
		}
		
		private static TreeMultimap<SortedSet<Integer>,Integer> mutableDuplicateOf(TreeMultimap<SortedSet<Integer>, Integer> rTerms) {
			TreeMultimap<SortedSet<Integer>,Integer> newRTerms = TreeMultimap.create(rTerms.keyComparator(), rTerms.valueComparator());
			newRTerms.putAll(rTerms);
			return newRTerms;
		}
		
		private static TreeMultimap<SortedSet<Integer>,Integer> mutableDuplicateOf(
				@Unmodified final TreeMultimap<SortedSet<Integer>,Integer> rTerms,
				@Unmodified final Set<Integer> newKnownRBar,
				@Modified	final Set<Integer> newKnownGBar) {
			
			if (newKnownRBar.isEmpty()) return mutableDuplicateOf(rTerms);
			
			TreeMultimap<SortedSet<Integer>,Integer> newRTerms = TreeMultimap.create(rTerms.asMap().comparator(),rTerms.valueComparator());
			Function<Set<Integer>,SortedSet<Integer>> filter = InverterUtils.sortedDiff(newKnownRBar);
			
			for (SortedSet<Integer> key : rTerms.keySet()) {
				SortedSet<Integer> test = filter.apply(key);
				if (test.isEmpty()) { 
					newKnownGBar.addAll(rTerms.get(key));
				} else {
					test = ImmutableSortedSet.copyOf(test);
					if (test.size() == 1) newKnownGBar.addAll(test);
					newRTerms.putAll( test, rTerms.get(key) );				
				}
			}

			newRTerms.values().removeAll(newKnownGBar);
						
			return newRTerms;
		}
		
		private static SortedSet<SortedSet<Integer>> mutableDuplicateOf(SortedSet<SortedSet<Integer>> terms) {
			SortedSet<SortedSet<Integer>> newTerms = Sets.newTreeSet(terms.comparator());
			for (SortedSet<Integer> term : terms) newTerms.add(Sets.newTreeSet(term));
			return newTerms;
		}
		
		private static SortedSet<SortedSet<Integer>> mutableDuplicateOf(
				@Unmodified	final SortedSet<SortedSet<Integer>> terms,
				@Unmodified	final Set<Integer> remove,
				@Modified	final Set<Integer> knownVar,
				@Modified	final SetMultimap<Edge,Integer> conflicts,
				@Modified	final Set<Integer> knownOtherBar,
				@Unmodified final Edge edge) {
			
			if (remove.isEmpty()) return mutableDuplicateOf(terms);
			
			SortedSet<SortedSet<Integer>> newTerms = Sets.newTreeSet(terms.comparator());
			Function<Set<Integer>,SortedSet<Integer>> filter = InverterUtils.sortedDiff(remove);
			for (SortedSet<Integer> term : terms) {
				SortedSet<Integer> test = filter.apply(term);
				if (test.isEmpty()) { conflicts.putAll(edge, intersection(remove,term)); }
				else if (test.size() == 1) { knownVar.addAll(test); }
				else { newTerms.add(test); }
			}
			
			InverterUtils.reduceTerms(newTerms,knownVar);
			
			if (knownOtherBar!=null) knownOtherBar.addAll(knownVar);
			
			return newTerms;			
		}
		
		private static void reinflate(
			TreeMultimap<SortedSet<Integer>,Integer> newRTerms,
			Set<Integer> rBar) {
			TreeMultimap<SortedSet<Integer>,Integer> inflate = TreeMultimap.create(newRTerms.keyComparator(), newRTerms.valueComparator());
			for (SortedSet<Integer> rTerm : newRTerms.keySet()) {
				for (Integer v : difference(newRTerms.get(rTerm),union(rTerm,rBar)))
					inflate.put(ImmutableSortedSet.<Integer>naturalOrder().addAll(rTerm).add(v).build(), v);
				for (SortedSet<Integer> superset : newRTerms.keySet().tailSet(SmallestFirst.markerSet(rTerm.size())) )
					if (superset.containsAll(rTerm)) inflate.putAll(superset, newRTerms.get(rTerm));
			}

			for (SortedSet<Integer> rTerm : inflate.keySet()) {
				newRTerms.putAll(rTerm, inflate.get(rTerm));
			}
		}
		
		private InvertVertex reprocessForOFF(final Set<Integer> onBefore) {
			TreeMultimap<SortedSet<Integer>,Integer> newRTerms = mutableDuplicateOf(rTerms);
			reinflate(newRTerms,rBar());
			Set<Integer> newKnownGBar = Sets.newHashSet(gBar());
						
			SetMultimap<Edge,Integer> conflicts = 
				processOffPrecursors(
					r(), 
					newKnownGBar, 
					rBar(), 
					g(), 
					newRTerms,
					onBefore
				);
						
			if (!conflicts.isEmpty()) return new Unsatisfiable();
			
			Set<Integer> newKnownG;
			SortedSet<SortedSet<Integer>> newGTerms;
			if (!gBar().containsAll(newKnownGBar)) {
				newKnownG = Sets.newHashSet();
				newGTerms = mutableDuplicateOf(gTerms,newKnownGBar,newKnownG,conflicts,null,NOT_TURN_ON);
				newKnownG.addAll(g());
			} else {
				newKnownG = g();
				newGTerms = gTerms;
			}

			if (!conflicts.isEmpty()) return new Unsatisfiable();
			
			Set<Integer> newKnownR = Sets.newHashSet();
			SortedSet<SortedSet<Integer>> newJustR = mutableDuplicateOf(justR);
			
			conflicts.putAll(
				finalizeOffPrecursors(
					newRTerms,
					newKnownR,
					newJustR,
					termPredicate(newKnownG,newGTerms)
				)
			);
			
			if (!conflicts.isEmpty()) return new Unsatisfiable(); //even possible?
			
			final ImmutableSetMultimap<Edge, Integer> fixedComponent = ImmutableSetMultimap.<Edge, Integer>builder()
			.putAll(TURN_ON , union(g(),newKnownG)) // a subset of knownRBar
			.putAll(TURN_OFF, union(r(),newKnownR)) // a subset of knownGBar
			.putAll(NOT_TURN_ON ,  union(gBar(),newKnownGBar)) // contains knownR
			.putAll(NOT_TURN_OFF,  rBar()) // contains knownG
			.build();
			
			//TODO adjust dynamic
			return new InvertVertex(true,fixedComponent,newGTerms,newJustR,newRTerms,dynamic,vertex);

		}
		
		private InvertVertex reprocessForON(Set<Integer> onBefore) {
			
			Set<Integer> newKnownG = Sets.newHashSet();
			SortedSet<SortedSet<Integer>> newGTerms = mutableDuplicateOf(gTerms);

			Set<Integer> newKnownRBar = Sets.newHashSet();
			
			SetMultimap<Edge,Integer> conflicts = 
				processOnPrecursors(
					r(),
					gBar(),
					newKnownRBar,newKnownG,newGTerms,
					onBefore
				);
						
			if (!conflicts.isEmpty()) return new Unsatisfiable();

			if (g().containsAll(newKnownG)) newKnownG.clear();
			if (rBar().containsAll(newKnownRBar)) newKnownRBar.clear();
			
			Set<Integer> newKnownR = Sets.newHashSet();
			Set<Integer> newKnownGBar = Sets.newHashSet();

			TreeMultimap<SortedSet<Integer>,Integer> newRTerms = mutableDuplicateOf(rTerms,newKnownRBar,newKnownGBar);
			SortedSet<SortedSet<Integer>> newJustR = mutableDuplicateOf(justR,newKnownRBar,newKnownR,conflicts,newKnownGBar,NOT_TURN_OFF);
					
			reinflate(newRTerms, union(rBar(),newKnownRBar));
						
			if (gBar().containsAll(newKnownGBar)) {
				newKnownGBar.clear();
			} else if (!disjoint(union(g(),newKnownG),newKnownGBar)) return new Unsatisfiable();
			
			newRTerms.values().removeAll(newKnownGBar);
			
			newKnownG.addAll(g());
			
			conflicts.putAll(
				finalizeOnPrecursors(
					newGTerms, newKnownGBar, newKnownG
				)
			);
			
			if (!conflicts.isEmpty()) return new Unsatisfiable();
						
			conflicts.putAll(
				finalizeOffPrecursors(
					newRTerms, newKnownR, newJustR, termPredicate(newKnownG,newGTerms) //old knownG already knocked out
				)
			);
						
			if (!conflicts.isEmpty()) return new Unsatisfiable(); //even possible?
						
			final ImmutableSetMultimap<Edge, Integer> fixedComponent = ImmutableSetMultimap.<Edge, Integer>builder()
			.putAll(TURN_ON , union(g(),newKnownG)) // a subset of knownRBar
			.putAll(TURN_OFF, union(r(),newKnownR)) // a subset of knownGBar
			.putAll(NOT_TURN_ON ,  union(gBar(),newKnownGBar)) // contains knownR
			.putAll(NOT_TURN_OFF,  union(rBar(),newKnownRBar)) // contains knownG
			.build();
						
			//TODO adjust dynamic
			return new InvertVertex(true,fixedComponent,newGTerms,newJustR,newRTerms,dynamic,vertex);
		}
		
		@Override public boolean equals(Object other) {
			if (other==this) return true;
			if (!(other instanceof InvertVertex)) return false;
			InvertVertex o = InvertVertex.class.cast(other);
			return o.isFeasible == isFeasible &&
				   o.vertex.equals(vertex) &&
				   o.fixedComponent.equals(fixedComponent) &&
				   o.gTerms.equals(gTerms) && 
				   o.rTerms.equals(rTerms) && 
				   o.justR.equals(justR);
		}
		
		@Override public int hashCode() {
			int hashcode = vertex;
			hashcode*=31; hashcode +=fixedComponent.hashCode();
			hashcode*=31; hashcode +=gTerms.hashCode();
			hashcode*=31; hashcode +=rTerms.hashCode();
			hashcode*=31; hashcode +=justR.hashCode();
			return hashcode;
		}
		
		static class NonSelfInhibit extends InvertVertex {
			
			NonSelfInhibit(Integer vertex) {
				super(true,
					ImmutableSetMultimap.<Edge,Integer>of(Edge.NOT_TURN_OFF,vertex),
					ImmutableSortedSet.<SortedSet<Integer>>of(),
					ImmutableSortedSet.<SortedSet<Integer>>of(),
					TreeMultimap.<SortedSet<Integer>,Integer>create(SmallestFirst.INSTANCE,Ordering.natural()),
					null,
					-1
				);
			}
			
		}
		
		static class Unsatisfiable extends InvertVertex {

			Unsatisfiable() {
				super(false,
					ImmutableSetMultimap.<Edge,Integer>of(),
					ImmutableSortedSet.<SortedSet<Integer>>of(),
					ImmutableSortedSet.<SortedSet<Integer>>of(),
					TreeMultimap.<SortedSet<Integer>,Integer>create(SmallestFirst.INSTANCE,Ordering.natural()),
					null,
					-1
				);
				// TODO add more info about unsatisfiability?
			}
			
			@Override public InvertVertex apply(Entry<SupportState,SupportState> newTransition) {
				return this;
			}
			
			@Override public InvertVertex apply(SupportState from, SupportState to) {
				return this;
			}

			
			@Override public boolean equals(Object o) {
				return o instanceof Unsatisfiable;
			}
			
			@Override public int hashCode() { return -1; }
			
		}

		public Set<Integer> not() { return Sets.intersection(gBar(), rBar()); }
		
	}

	public static InvertVertex fromKnown(ImmutableSetMultimap<Edge, Integer> known) {
		// TODO use known as fixed, everything else is unfixed
		return null;
	}
	
}
